﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.UserData;

namespace DIS.WindowsPhone.Toolkit.Controls
{
    /// <summary>
    /// Represents a list of the phone's contact that can be used to select a contact regardless of whether they have a phone number or email
    /// </summary>
    public class ContactPicker : ItemsControl
    {
        private PhoneApplicationFrame _frame;
        private object _frameContentWhenOpened;
        /// <summary>
        /// Whether this list picker has the picker page opened.
        /// </summary>
        private bool _hasPickerPageOpen;
        private ContactPickerPage _contactPickerPage;
        private Contact _deferredSelectedItem = null;
        private int _deferredSelectedIndex = -1;
        private bool _updatingSelection;

        /// <summary>
        /// Event that is raised when the selection changes.
        /// </summary>
        public event SelectionChangedEventHandler SelectionChanged;

        /// <summary>
        /// Gets or sets the Uri to use for loading the ContactPickerPage instance when the control is tapped.
        /// </summary>
        public Uri PickerPageUri
        {
            get { return (Uri)GetValue(PickerPageUriProperty); }
            set { SetValue(PickerPageUriProperty, value); }
        }

        /// <summary>
        /// Identifies the PickerPageUri DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PickerPageUriProperty = DependencyProperty.Register(
            "PickerPageUri", typeof(Uri), typeof(ContactPicker), null);

        /// <summary>
        /// Gets or sets the text to use for the header
        /// </summary>
        public string HeaderText
        {
            get { return (string)GetValue(HeaderTextProperty); }
            set { SetValue(HeaderTextProperty, value); }
        }

        /// <summary>
        /// Identifies the HeaderText DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty HeaderTextProperty = DependencyProperty.Register(
            "HeaderText", typeof(string), typeof(ContactPicker), null);

        /// <summary>
        /// Gets or sets the text to use for the search icon
        /// </summary>
        public string SearchText
        {
            get { return (string)GetValue(SearchTextProperty); }
            set { SetValue(SearchTextProperty, value); }
        }

        /// <summary>
        /// Identifies the SearchText DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register(
            "SearchText", typeof(string), typeof(ContactPicker), null);

        /// <summary>
        /// Gets or sets the selected item.
        /// </summary>
        public Contact SelectedItem
        {
            get { return (Contact)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        /// <summary>
        /// Identifies the SelectedItem DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(Contact), typeof(ContactPicker), new PropertyMetadata(null, OnSelectedItemChanged));

        private static void OnSelectedItemChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((ContactPicker)o).OnSelectedItemChanged(e.OldValue, e.NewValue);
        }

        [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SelectedItem", Justification = "Property name.")]
        private void OnSelectedItemChanged(object oldValue, object newValue)
        {
            if (null == Items || Items.Count == 0)
            {
                if (null == Template)
                {
                    // Can't set the value now; remember it for later
                    _deferredSelectedItem = newValue as Contact;
                    return;
                }
                else
                {
                    throw new InvalidOperationException("SelectedItem must always be set to a valid value.");
                }
            }

            // Validate new value
            int newValueIndex = (null != newValue) ? Items.IndexOf((Contact)newValue) : -1;

            if ((-1 == newValueIndex) && (0 < Items.Count))
            {
                throw new InvalidOperationException("SelectedItem must always be set to a valid value.");
            }

            // Synchronize SelectedIndex property
            if (!_updatingSelection)
            {
                _updatingSelection = true;
                SelectedIndex = newValueIndex;
                _updatingSelection = false;
            }

            // Fire SelectionChanged event
            var handler = SelectionChanged;
            if (null != handler)
            {
                IList removedItems = (null == oldValue) ? new object[0] : new object[] { oldValue };
                IList addedItems = (null == newValue) ? new object[0] : new object[] { newValue };
                handler(this, new SelectionChangedEventArgs(removedItems, addedItems));
            }
        }

        /// <summary>
        /// Gets or sets the index of the selected item.
        /// </summary>
        public int SelectedIndex
        {
            get { return (int)GetValue(SelectedIndexProperty); }
            set { SetValue(SelectedIndexProperty, value); }
        }

        /// <summary>
        /// Identifies the SelectedIndex DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty SelectedIndexProperty =
            DependencyProperty.Register("SelectedIndex", typeof(int), typeof(ContactPicker), new PropertyMetadata(-1, OnSelectedIndexChanged));

        private static void OnSelectedIndexChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((ContactPicker)o).OnSelectedIndexChanged((int)e.OldValue, (int)e.NewValue);
        }

        [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SelectedIndex", Justification = "Property name.")]
        private void OnSelectedIndexChanged(int oldValue, int newValue)
        {
            // Validate new value
            if ((Items.Count <= newValue) ||
                ((0 < Items.Count) && (newValue < 0)) ||
                ((0 == Items.Count) && (newValue != -1)))
            {
                if ((null == Template) && (0 <= newValue))
                {
                    // Can't set the value now; remember it for later
                    _deferredSelectedIndex = newValue;
                    return;
                }
                throw new InvalidOperationException("SelectedIndex must always be set to a valid value.");
            }

            // Synchronize SelectedItem property
            if (!_updatingSelection)
            {
                _updatingSelection = true;
                SelectedItem = (-1 != newValue) ? Items[newValue] : null;
                _updatingSelection = false;
            }

            if (-1 != oldValue)
            {
                // Toggle container selection
                ContactPickerItem oldContainer = (ContactPickerItem)ItemContainerGenerator.ContainerFromIndex(oldValue);
                if (null != oldContainer)
                {
                    oldContainer.IsSelected = false;
                }
            }
        }

        public ContactPicker()
        {
            DefaultStyleKey = typeof(ContactPicker);

            Unloaded += OnUnloaded;
        }

        private void OnUnloaded(object sender, RoutedEventArgs e)
        {
            // Unhook any remaining event handlers
            if (null != _frame)
            {
                //_frame.ManipulationStarted -= OnFrameManipulationStarted;
                _frame = null;
            }
        }

        private void OpenPickerPage()
        {
            if (null == PickerPageUri)
            {
                throw new ArgumentException("PickerPageUri");
            }

            if (null == _frame)
            {
                // Hook up to necessary events and navigate
                _frame = Application.Current.RootVisual as PhoneApplicationFrame;
                if (null != _frame)
                {
                    _frameContentWhenOpened = _frame.Content;

                    // Save and clear host page transitions for the upcoming "popup" navigation
                    UIElement frameContentWhenOpenedAsUIElement = _frameContentWhenOpened as UIElement;

                    _frame.Navigated += OnFrameNavigated;

                    _hasPickerPageOpen = true;

                    _frame.Navigate(PickerPageUri);
                }
            }
        }

        private void ClosePickerPage()
        {
            // Unhook from events
            if (null != _frame)
            {
                _frame.Navigated -= OnFrameNavigated;

                // Restore host page transitions for the completed "popup" navigation
                UIElement frameContentWhenOpenedAsUIElement = _frameContentWhenOpened as UIElement;

                if (null != frameContentWhenOpenedAsUIElement)
                {
                    //TransitionService.SetNavigationInTransition(frameContentWhenOpenedAsUIElement, _savedNavigationInTransition);
                    //_savedNavigationInTransition = null;
                    //TransitionService.SetNavigationOutTransition(frameContentWhenOpenedAsUIElement, _savedNavigationOutTransition);
                    //_savedNavigationOutTransition = null;
                }

                _frame = null;
                _frameContentWhenOpened = null;
            }

            // Commit the value if available
            if (null != _contactPickerPage)
            {
                if (null != _contactPickerPage.SelectedItem)
                {
                    SelectedItem = _contactPickerPage.SelectedItem;
                }
                _contactPickerPage = null;
            }
        }

        /// <summary>
        /// Identifies the SelectedIndex DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty ItemsProperty =
            DependencyProperty.Register("ItemsProperty", typeof(List<Contact>), typeof(ContactPicker), null);

        public new List<Contact> Items
        {
            get
            {
                return (List<Contact>)GetValue(ItemsProperty);
            }
            set
            {
                SetValue(ItemsProperty, value);
            }
        }

        private void OnFrameNavigated(object sender, NavigationEventArgs e)
        {
            if (e.Content == _frameContentWhenOpened)
            {
                // Navigation to original page; close the picker page
                ClosePickerPage();
            }
            else if (null == _contactPickerPage && _hasPickerPageOpen)
            {
                _hasPickerPageOpen = false;
                _contactPickerPage = e.Content as ContactPickerPage;
                if (null != _contactPickerPage)
                {
                    _contactPickerPage.Contacts.Clear();
                    if (null != Items)
                    {
                        _contactPickerPage.Contacts.AddRange(Items);
                    }

                    _contactPickerPage.SelectedItem = SelectedItem;
                    _contactPickerPage.HeaderText = HeaderText;
                    _contactPickerPage.SearchText = SearchText;
                }
            }
        }

        /// <summary>
        /// Builds the visual tree for the control when a new template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            // Unhook from old elements
            //if (null != _itemsPresenterHostParent)
            //{
            //    _itemsPresenterHostParent.SizeChanged -= OnItemsPresenterHostParentSizeChanged;
            //}
            //_storyboard.Stop();

            base.OnApplyTemplate();

            //// Hook up to new elements
            //_itemsPresenterPart = GetTemplateChild(ItemsPresenterPartName) as ItemsPresenter;
            //_itemsPresenterTranslateTransformPart = GetTemplateChild(ItemsPresenterTranslateTransformPartName) as TranslateTransform;
            //_itemsPresenterHostPart = GetTemplateChild(ItemsPresenterHostPartName) as Canvas;
            //_itemsPresenterHostParent = (null != _itemsPresenterHostPart) ? _itemsPresenterHostPart.Parent as FrameworkElement : null;
            //_multipleSelectionModeSummary = GetTemplateChild(MultipleSelectionModeSummaryPartName) as TextBlock;
            //_border = GetTemplateChild(BorderPartName) as Border;

            //if (null != _itemsPresenterHostParent)
            //{
            //    _itemsPresenterHostParent.SizeChanged += OnItemsPresenterHostParentSizeChanged;
            //}
            //if (null != _itemsPresenterHostPart)
            //{
            //    Storyboard.SetTarget(_heightAnimation, _itemsPresenterHostPart);
            //    if (!_storyboard.Children.Contains(_heightAnimation))
            //    {
            //        _storyboard.Children.Add(_heightAnimation);
            //    }
            //}
            //else
            //{
            //    if (_storyboard.Children.Contains(_heightAnimation))
            //    {
            //        _storyboard.Children.Remove(_heightAnimation);
            //    }
            //}
            //if (null != _itemsPresenterTranslateTransformPart)
            //{
            //    Storyboard.SetTarget(_translateAnimation, _itemsPresenterTranslateTransformPart);
            //    if (!_storyboard.Children.Contains(_translateAnimation))
            //    {
            //        _storyboard.Children.Add(_translateAnimation);
            //    }
            //}
            //else
            //{
            //    if (_storyboard.Children.Contains(_translateAnimation))
            //    {
            //        _storyboard.Children.Remove(_translateAnimation);
            //    }
            //}


            // Commit deferred SelectedIndex (if any)
            if (-1 != _deferredSelectedIndex)
            {
                SelectedIndex = _deferredSelectedIndex;
                _deferredSelectedIndex = -1;
            }
            if (null != _deferredSelectedItem)
            {
                SelectedItem = _deferredSelectedItem;
                _deferredSelectedItem = null;
                OnSelectedItemChanged(SelectedItem, SelectedItem);
            }
        }

        public void Show()
        {
            OpenPickerPage();
        }
    }
}
